/*
 * @Author: wuqingchun
 * @Date: 2024-07-23 19:48:58
 * @Description: 
 * @LastEditors: wuqingchun
 * @LastEditTime: 2024-07-26 17:26:53
 */

#include "central_cache.h"
#include "page_cache.h"


CentralCache CentralCache::m_objInstance = {};

size_t CentralCache::FetchRangeObj(void*& refStartPtr, void*& refEndPtr, size_t ulObjNum, size_t ulObjSize)
{
    //中心缓存和thread cache的哈希结构是一样的使用同样的方式确定下标
    size_t ulIndex = SizeAlignStrategy::Index(ulObjSize);

    size_t ulAllocNum = __AllocObjectsFromList(ulIndex, ulObjNum, refStartPtr, refEndPtr);
    if (ulAllocNum > 0)
    {
        return ulAllocNum;
    }

    return __AllocObjectsFromPGCache(ulIndex, ulObjNum, ulObjSize, refStartPtr, refEndPtr);
}

size_t CentralCache::__AllocObjectsFromList(size_t ulIndex, size_t ulObjNum, void*& refStartPtr, void*& refEndPtr)
{
    std::lock_guard<std::mutex> objLG(m_arrMtx[ulIndex]);
    SpanList& refList = m_arrSpanList[ulIndex];
    Span* pstFind = nullptr;
    Span* pstSpan = refList.Begin();
    while (pstSpan != refList.End())
    {
        if (pstSpan->pFreeList != nullptr)
        {
            pstFind = pstSpan;
            break;
        }
        
        pstSpan = pstSpan->pstNext;
    }

    if (pstFind == nullptr)
    {
        return 0;
    }

    return __GetObjectsFromSpan(*pstFind, ulObjNum, refStartPtr, refEndPtr);
}

size_t CentralCache::__AllocObjectsFromPGCache(size_t ulIndex, size_t ulObjNum, size_t ulObjSize, void*& refStartPtr, void*& refEndPtr)
{
    // 从页缓存中申请一大块连续内存(Span)，包含多个Page
    Span* pstSpan = PageCache::GetInstance()->NewSpan(SizeAlignStrategy::CalculatePageNum(ulObjSize));
    if (pstSpan == nullptr)
    {
        return 0;
    }

    // 对获取的span进行切分，不需要加锁，因为其他线程访问不到
    // 由于mmap申请的内存地址能够保证按页对齐，因此地址右移PAGE_SHIFT为页编号，页编号左移可以得到内存地址
    char* pcStart = (char*)(pstSpan->ulPageID << PAGE_SHIFT);
    char* pcEnd = pcStart + pstSpan->ulPageNum * PAGE_SIZE - ulObjSize;
    pstSpan->pFreeList = pcStart;

    char* pcTail = pcStart + ulObjSize;
    
    // 计算span的大块内存的起始地址和大块内存的大小(字节数)
    while (pcStart <= pcEnd)
    {
        *(char**)pcStart = pcTail;
        pcStart = pcTail;
        pcTail += ulObjSize; 
    }

    *(char**)pcStart = nullptr;

    std::lock_guard<std::mutex> objLG(m_arrMtx[ulIndex]);
    m_arrSpanList[ulIndex].PushFront(pstSpan);
    
    return __GetObjectsFromSpan(*pstSpan, ulObjNum, refStartPtr, refEndPtr);
}

size_t CentralCache::__GetObjectsFromSpan(Span& refSpan, size_t ulObjNum, void*& refStartPtr, void*& refEndPtr)
{
    size_t ulAllocNum = 1;
    refStartPtr = refSpan.pFreeList;
    refEndPtr = refStartPtr;

    //从Span中获取num个内存块
    //span中可能不够num个 有多少申请多少 
    //span可能很多 最多要batch_num个(thread cache一次批量申请的个数)

    while (FreeList::GetNextObject(refEndPtr) != nullptr && ulAllocNum < ulObjNum)
    {
        refEndPtr = FreeList::GetNextObject(refEndPtr);
    }

    refSpan.pFreeList = FreeList::GetNextObject(refEndPtr);
    refSpan.bIsUse = true;
    refSpan.ulUseCount += ulAllocNum; 

    FreeList::ClearNextObject(refEndPtr);

    return ulAllocNum;
}